//
// Created by pzw on 24-3-1.
//

#include "HashSet.h"

template<typename Value, typename Hash, typename Pred, typename Alloc>
HashSet<Value, Hash, Pred, Alloc>::HashSet(
        std::unordered_set<Value, Hash, Pred, Alloc> *set) : set(set) {
}

template<typename Value, typename Hash, typename Pred, typename Alloc>
HashSet<Value, Hash, Pred, Alloc>::~HashSet() {
    delete set;
}

template<typename Value, typename Hash, typename Pred, typename Alloc>
std::unordered_set<Value, Hash, Pred, Alloc> *HashSet<Value, Hash, Pred, Alloc>::cset() {
    return set;
}

template<typename Value, typename Hash, typename Pred, typename Alloc>
bool HashSet<Value, Hash, Pred, Alloc>::add(Value e) {
    return (*set).insert(e).second;
}

template<typename Value, typename Hash, typename Pred, typename Alloc>
void HashSet<Value, Hash, Pred, Alloc>::clear() {
    (*set).clear();
}

template<class Value, class Hash, class Pred, class Alloc>
HashSet<Value, Hash, Pred, Alloc> HashSet<Value, Hash, Pred, Alloc>::clone() {
    HashSet<Value, Hash, Pred, Alloc> newSet;
    for (const auto &val: *this) {
        newSet.insert(val);
    }
    return newSet;
}

template<typename Value, typename Hash, typename Pred, typename Alloc>
bool HashSet<Value, Hash, Pred, Alloc>::contains(Value o) {
    return (*set).find(o) != (*set).end();
}

template<typename Value, typename Hash, typename Pred, typename Alloc>
bool HashSet<Value, Hash, Pred, Alloc>::isEmpty() {
    return (*set).empty();
}

template<typename Value, typename Hash, typename Pred, typename Alloc>
HashSetIterator<Value> HashSet<Value, Hash, Pred, Alloc>::iterator() {
    return HashSetIterator<Value>(*set);
}

template<typename Value, typename Hash, typename Pred, typename Alloc>
bool HashSet<Value, Hash, Pred, Alloc>::remove(Value o) {
    return false;
}

template<typename Value, typename Hash, typename Pred, typename Alloc>
size_t HashSet<Value, Hash, Pred, Alloc>::size() {
    return (*set).size();
}

template<class Value, class Hash, class Pred, class Alloc>
std::vector<Value> HashSet<Value, Hash, Pred, Alloc>::toArray() {
    std::vector<Value> result;
    for (const Value &val: *this) {
        result.push_back(val);
    }
    return result;
}

template<typename Value, typename Hash, typename Pred, typename Alloc>
template<typename T>
std::vector<T> HashSet<Value, Hash, Pred, Alloc>::toArray2(std::vector<T> &a) {
    a.resize(set->size());
    std::copy(set->begin(), set->end(), a.begin());
    return a;
}

template<class Value, class Hash, class Pred, class Alloc>
bool HashSet<Value, Hash, Pred, Alloc>::containsAll(const std::unordered_set<Value, Hash, Pred, Alloc> &c) {
    for (const Value &val: c) {
        if (set->find(val) == set->end()) {
            return false;
        }
    }
    return true;
}

template<class Value, class Hash, class Pred, class Alloc>
bool HashSet<Value, Hash, Pred, Alloc>::addAll(const std::unordered_set<Value, Hash, Pred, Alloc> &c) {
    bool modified = false;
    for (const Value &val: c) {
        if (set->insert(val).second) {
            modified = true;
        }
    }
    return modified;
}

template<class Value, class Hash, class Pred, class Alloc>
bool HashSet<Value, Hash, Pred, Alloc>::removeAll(const std::unordered_set<Value, Hash, Pred, Alloc> &c) {
    bool modified = false;
    for (const Value &val: c) {
        if (set->erase(val) > 0) {
            modified = true;
        }
    }
    return modified;
}

template<class Value, class Hash, class Pred, class Alloc>
bool HashSet<Value, Hash, Pred, Alloc>::retainAll(const std::unordered_set<Value, Hash, Pred, Alloc> &c) {
    bool modified = false;
    for (auto it = set->begin(); it != set->end();) {
        if (c.find(*it) == c.end()) {
            it = set->erase(it);
            modified = true;
        } else {
            ++it;
        }
    }
    return modified;
}

template<class Value, class Hash, class Pred, class Alloc>
bool HashSet<Value, Hash, Pred, Alloc>::equals(const HashSet<Value, Hash, Pred, Alloc> &o) {
    if (set->size() != o.size()) {
        return false;
    }
    for (const Value &val: o) {
        if (set->find(val) == set->end()) {
            return false;
        }
    }
    return true;
}

template<class Value, class Hash, class Pred, class Alloc>
int HashSet<Value, Hash, Pred, Alloc>::hashCode() {
    int hash = 0;
    for (const Value &val: *this) {
        hash += std::hash<Value>{}(val);
    }
    return hash;
}

template<class Value, class Hash, class Pred, class Alloc>
void HashSet<Value, Hash, Pred, Alloc>::forEach(std::function<void(const Value &)> action) {
    for (const Value &val: *set) {
        action(val);
    }
}

template<class Value, class Hash, class Pred, class Alloc>
std::string HashSet<Value, Hash, Pred, Alloc>::toString() {
    std::string result = "{";
    for (auto it = set->begin(); it != set->end(); ++it) {
        result += std::to_string(*it);
        if (std::next(it) != set->end()) {
            result += ", ";
        }
    }
    result += "}";
    return result;
}

/**
 * <p>
 * Q: 在C++中：
 * int t = 5; 是不是表示分配一个内存，假设地址0x0a，别名为t，存储得值为5？
 * int *x = &t; 是不是表示分配一个内存，假设值地址0x0b，别名为x，存储的值为0x0a？
 *
 * 对于变量t、x，是不是在编译后，就已经不存在了？只是对于程序员来说它是变量t、x；
 * 但是对于编译器来说，编译后，就已经成为一个固定地址 t -> 0x0a，x -> 0x0b了？
 * </p>
 *
 * <p>
 * answer:
 * 您对C++中变量和指针的理解基本正确，让我进一步解释和评价：
 *
 * 1. int t = 5;  确实表示在内存中分配了一个整数变量 `t` ，并将值 `5` 存储在这个变量中。
 *     编译器会为变量 `t` 分配内存空间，并在内存中标记地址（例如0x0a），其中存储着值 `5` 。这部分理解是正确的。
 *
 * 2. int *x = &t;  确实表示定义了一个整型指针变量 `x` ，并将变量 `t` 的地址（0x0a）赋值给了这个指针变量 `x` 。
 *    指针变量 `x` 存储的值是变量 `t` 的地址。这部分也是正确的。
 *
 * 3. 对于变量 `t` 、 `x` ，在编译后，它们的内存空间确实会存在，但是在编译后生成的可执行程序中不再有 `t` 、 `x` 这样的变量名。
 *    实际上，编译器会将变量名映射到内存地址，程序在运行时会根据这些地址来访问和操作变量的值。
 *    因此，对于编译器来说，变量名 `t` 、 `x` 在编译后会被转换为固定的内存地址，而程序员可以通过这些变量名来操作对应的内存空间。这部分也是正确的。
 * </p>
 *
 * <p>
 * 综上：对于int t = 5; int *x = &t;
 * 这样的语句，其实本质，就是在请求分配内存：
 * 对计算机而言，会将t、x映射成固定地址；
 * 对程序员而言，t、x就是内存地址的别名，程序员其实已经通过变量的名字，拿到了内存地址，拿到了内存地址，就等于拿到了内存，拿到内存就等于拿到了内存存储的值；
 * 本质上，程序员在编程时，每一个指令，都是在通过变量名，间接地操作着内存；
 * </p>
 *
 * @return exit_code
 */
int HashSet_main() {
    HashSet<int> *hashSet = new HashSet<int>(new std::unordered_set<int>());
    std::unordered_set<int> *uset = new std::unordered_set<int>();
    HashSet<int> hashSet2(uset);
    HashSet<int> (*hashSet3) = new HashSet<int>(new std::unordered_set<int>());

    for (int i = 0; i < 8; ++i) {
        hashSet->add(i);
        hashSet2.add(i);
        hashSet3->add(i);
    }

    if (hashSet->contains(7)) {
        std::cout << "HashSet_main: hashSet contains 7" << std::endl;
    }

    if (!hashSet->containsAll(*(hashSet2.cset()))) {
        hashSet->addAll(*(hashSet2.cset()));
    }
    hashSet->removeAll(*(hashSet3->cset()));

    HashSetIterator<int> it = hashSet->iterator();
    while (it.hasNext()) {
        int next = it.next();
        if (next == 7) {
            it.remove();
        }
    }

    hashSet->forEach([](const int &each) -> void {
        std::cout << "HashSet_main: forEach = " << each << std::endl;
    });
    std::cout << "HashSet_main: toString = " << hashSet->toString() << std::endl;

    delete hashSet;
    delete hashSet3;
    return 0;
}

